/************************************************************
** @brief   : simple_page
** @author  : vandoul
** @github  : https://gitee.com/vandoul
** @date    : 2022-01
** @version : v1.0.0
** @note    : simple_page.c
***********************************************************/

#include "simple_page.h"

struct _simple_page_page_link {
    struct _simple_page_page_link *parent;
    struct _simple_page_page_link *child;
    struct _simple_page_page_link *prev;
    struct _simple_page_page_link *next;
};

struct _simple_page_page {
    struct _simple_page_page_link link;
    char name[SIMPLE_PAGE_PAGE_NAME_SIZE];
    simple_page_page_event_callback_t fn_evt_cb_tab[SIMPLE_PAGE_PAGE_EVENT_NUM];
    void *fn_evt_param_tab[SIMPLE_PAGE_PAGE_EVENT_NUM];
};

struct _simple_page {
    simple_page_event_wait_t event_wait;
    simple_page_page_link_t home_link;
    simple_page_page_link_t current_page_link;
    simple_page_event_callback_t fn_evt_cb_tab[SIMPLE_PAGE_EVENT_NUM];
    void *fn_evt_param_tab[SIMPLE_PAGE_EVENT_NUM];
};

#define SIMPLE_PAGE_PAGE_SIZE      sizeof(struct _simple_page_page)
#define SIMPLE_PAGE_SIZE           sizeof(struct _simple_page)
    
#define SIMPLE_PAGE_PAGE_DO_EVENT_CB(p, pp, e)                  do{if((pp)&&(pp)->fn_evt_cb_tab[e]){(pp)->fn_evt_cb_tab[e](p, pp, e, pp->fn_evt_param_tab[e]);}}while(0)
#define SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(p, l, e)         do{if(l){simple_page_page_t pp=simple_page_page_get_from_link(l);if((pp)->fn_evt_cb_tab[e]){(pp)->fn_evt_cb_tab[e](p, pp, e, pp->fn_evt_param_tab[e]);}}}while(0)
#define SIMPLE_PAGE_PAGE_EVENT_CB_IS_EMPTY(pp,e)                (pp->fn_evt_cb_tab[e] == RT_NULL)
    
#define SIMPLE_PAGE_DO_EVENT_CB(p, e)                           do{if((p)&&(p)->fn_evt_cb_tab[e]){(p)->fn_evt_cb_tab[e](p, e, p->fn_evt_param_tab[e]);}}while(0)
// simple_page_page_link
inline static void simple_page_page_link_init(simple_page_page_link_t link)
{
    link->parent = link->child = link;
    link->next = link->prev = link;
}

inline static void simple_page_page_link_add_parent(simple_page_page_link_t link, simple_page_page_link_t page_link)
{
    page_link->child = link;
    page_link->parent = link->parent;
    link->parent->child = page_link;
    link->parent = page_link;
}

inline static void simple_page_page_link_add_child(simple_page_page_link_t link, simple_page_page_link_t page_link)
{
    page_link->child = link->child;
    page_link->parent = link;
    link->child->parent = page_link;
    link->child = page_link;
}

inline static void simple_page_page_link_add_next(simple_page_page_link_t link, simple_page_page_link_t next)
{
    //next->parent = link->parent; //parent copy
    
    next->next = link->next;
    next->prev = link;
    
    next->next->prev = next;
    link->next = next;
}

inline static void simple_page_page_link_add_prev(simple_page_page_link_t link, simple_page_page_link_t prev)
{
    //prev->parent = link->parent; //parent copy
    
    prev->next = link;
    prev->prev = link->prev;
    
    link->prev->next = prev;
    link->prev = prev;
}

inline static simple_page_page_link_t simple_page_page_link_get_parent(simple_page_page_link_t link)
{
    return link->parent;
}

inline static simple_page_page_link_t simple_page_page_link_get_child(simple_page_page_link_t link)
{
    return link->child;
}

inline static simple_page_page_link_t simple_page_page_link_get_next(simple_page_page_link_t link)
{
    return link->next;
}

inline static simple_page_page_link_t simple_page_page_link_get_prev(simple_page_page_link_t link)
{
    return link->prev;
}

inline static void simple_page_page_link_remove_prev_and_next(simple_page_page_link_t link)
{
    link->prev->next = link->next;
    link->next->prev = link->prev;
    link->next = link->prev = link;
}

inline static void simple_page_page_link_remove_parent_and_child(simple_page_page_link_t link)
{
    link->parent->child = link->child;
    link->child->parent = link->parent;
    link->parent = link->child = link;
}

RT_UNUSED inline static bool simple_page_page_link_parent_child_is_empty(simple_page_page_link_t link)
{
    if((link == link->parent) || (link == link->child)) {
        return true;
    }
    return false;
}

RT_UNUSED inline static bool simple_page_page_link_prev_next_is_empty(simple_page_page_link_t link)
{
    if((link == link->prev) || (link == link->next)) {
        return true;
    }
    return false;
}

RT_UNUSED inline static bool simple_page_page_link_is_empty(simple_page_page_link_t link)
{
    if(simple_page_page_link_parent_child_is_empty(link) || simple_page_page_link_prev_next_is_empty(link)) {
        return true;
    }
    return false;
}

RT_UNUSED inline static bool simple_page_page_link_is_root(simple_page_page_link_t link)
{
    if(link == link->parent) {
        return true;
    }
    return false;
}

// simple_page_page
inline static simple_page_page_t simple_page_page_get_from_link(simple_page_page_link_t link)
{
    return rt_container_of(link, struct _simple_page_page, link);
}

inline static simple_page_page_link_t simple_page_page_get_to_link(simple_page_page_t page)
{
    return &page->link;
}

simple_page_page_t simple_page_page_create(const char *name, simple_page_page_event_callback_t draw, void *fn_param)
{
    simple_page_page_t page;
    page = rt_malloc(SIMPLE_PAGE_PAGE_SIZE);
    if(page == RT_NULL) {
        return RT_NULL;
    }
    rt_memset(page, 0, SIMPLE_PAGE_PAGE_SIZE);
    simple_page_page_link_init(&page->link);
    page->fn_evt_param_tab[SIMPLE_PAGE_PAGE_EVENT_DRAW] = fn_param;
    page->fn_evt_cb_tab[SIMPLE_PAGE_PAGE_EVENT_DRAW] = draw;
    rt_strncpy(page->name, name, SIMPLE_PAGE_PAGE_NAME_SIZE-1);
    return page;
}

void simple_page_page_rename(simple_page_page_t page, const char *name)
{
    if(page == RT_NULL) {
        return ;
    }
    rt_strncpy(page->name, name, SIMPLE_PAGE_PAGE_NAME_SIZE-1);
}

const char *simple_page_page_get_name(simple_page_page_t page)
{
    if(page == RT_NULL) {
        return "";
    }
    return page->name;
}

void simple_page_page_add_event(simple_page_page_t page, simple_page_page_event_t evt, simple_page_page_event_callback_t cb, void *param)
{
    if(page == RT_NULL) {
        return ;
    }
    if(IS_SIMPLE_PAGE_PAGE_EVENT(evt)) {
        if(evt == SIMPLE_PAGE_PAGE_EVENT_ALL) {
            for(int i=0; i<SIMPLE_PAGE_PAGE_EVENT_NUM; i++) {
                page->fn_evt_cb_tab[i] = cb;
                page->fn_evt_param_tab[i] = param;
            }
        } else {
            page->fn_evt_cb_tab[evt] = cb;
            page->fn_evt_param_tab[evt] = param;
        }
    }
}

void simple_page_page_remove_event(simple_page_page_t page, simple_page_page_event_t evt)
{
    simple_page_page_add_event(page, evt, RT_NULL, RT_NULL);
}

void simple_page_page_add_next(simple_page_page_t page, simple_page_page_t next)
{
    if((next == RT_NULL) || (page == RT_NULL)) {
        return ;
    }
    simple_page_page_link_add_next(&page->link, &next->link);
}

void simple_page_page_add_prev(simple_page_page_t page, simple_page_page_t prev)
{
    if((prev == RT_NULL) || (page == RT_NULL)) {
        return ;
    }
    simple_page_page_link_add_prev(&page->link, &prev->link);
}

void simple_page_page_add_child(simple_page_page_t page, simple_page_page_t child)
{
    if((child == RT_NULL) || (page == RT_NULL)) {
        return ;
    }
    simple_page_page_link_add_child(&page->link, &child->link);
}

void simple_page_page_add_parent(simple_page_page_t page, simple_page_page_t parent)
{
    if((parent == RT_NULL) || (page == RT_NULL)) {
        return ;
    }
    simple_page_page_link_add_parent(&page->link, &parent->link);
}

void simple_page_page_remove_parent_child(simple_page_page_t page)
{
    if(page == RT_NULL) {
        return ;
    }
    simple_page_page_link_remove_parent_and_child(&page->link);
}

void simple_page_page_remove_prev_next(simple_page_page_t page)
{
    if(page == RT_NULL) {
        return ;
    }
    simple_page_page_link_remove_prev_and_next(&page->link);
}

void simple_page_page_remove(simple_page_page_t page)
{
    simple_page_page_remove_parent_child(page);
    simple_page_page_remove_prev_next(page);
}

simple_page_page_t simple_page_page_get_next(simple_page_page_t page_page)
{
    if(page_page == RT_NULL) {
        return RT_NULL;
    }
    return simple_page_page_get_from_link(page_page->link.next);
}

simple_page_page_t simple_page_page_get_prev(simple_page_page_t page_page)
{
    if(page_page == RT_NULL) {
        return RT_NULL;
    }
    return simple_page_page_get_from_link(page_page->link.prev);
}

simple_page_page_t simple_page_page_get_child(simple_page_page_t page_page)
{
    if(page_page == RT_NULL) {
        return RT_NULL;
    }
    return simple_page_page_get_from_link(page_page->link.child);
}

simple_page_page_t simple_page_page_get_parent(simple_page_page_t page_page)
{
    if(page_page == RT_NULL) {
        return RT_NULL;
    }
    return simple_page_page_get_from_link(page_page->link.parent);
}

static void simple_page_process_page_event(simple_page_t page, simple_page_event_t evt)
{
    switch(evt) {
        case SIMPLE_PAGE_EVENT_DESTROY:
        {
        }
        break;
        case SIMPLE_PAGE_EVENT_ALARM:
        {
            SIMPLE_PAGE_DO_EVENT_CB(page, SIMPLE_PAGE_EVENT_ALARM);
        }
        break;
        default:
        {
        }
        break;
    }
}
// simple_page
void simple_page_process(simple_page_t page)
{
    simple_page_page_event_t evt = page->event_wait();
    if(page->home_link == RT_NULL) {
        return ;
    }
    simple_page_page_t temp_page;
    if(page->current_page_link == RT_NULL) {
        page->current_page_link = page->home_link;
        SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_IN);
    }
    simple_page_page_link_t temp_link = RT_NULL;
    temp_page = simple_page_page_get_from_link(page->current_page_link);
    switch(evt) {
        case SIMPLE_PAGE_PAGE_EVENT_HOME: {
            if(SIMPLE_PAGE_PAGE_EVENT_CB_IS_EMPTY(temp_page, SIMPLE_PAGE_PAGE_EVENT_HOME)) {
                temp_link = page->home_link;
            } else {
                SIMPLE_PAGE_PAGE_DO_EVENT_CB(page, temp_page, SIMPLE_PAGE_PAGE_EVENT_HOME);
            }
        }
        break;
        case SIMPLE_PAGE_PAGE_EVENT_PREV: {
            if(SIMPLE_PAGE_PAGE_EVENT_CB_IS_EMPTY(temp_page, SIMPLE_PAGE_PAGE_EVENT_PREV)) {
                temp_link = simple_page_page_link_get_next(page->current_page_link);
            } else {
                SIMPLE_PAGE_PAGE_DO_EVENT_CB(page, temp_page, SIMPLE_PAGE_PAGE_EVENT_PREV);
            }
        }
        break;
        case SIMPLE_PAGE_PAGE_EVENT_NEXT: {
            if(SIMPLE_PAGE_PAGE_EVENT_CB_IS_EMPTY(temp_page, SIMPLE_PAGE_PAGE_EVENT_NEXT)) {
                temp_link = simple_page_page_link_get_next(page->current_page_link);
            } else {
                SIMPLE_PAGE_PAGE_DO_EVENT_CB(page, temp_page, SIMPLE_PAGE_PAGE_EVENT_NEXT);
            }
        }
        break;
        case SIMPLE_PAGE_PAGE_EVENT_BACK: {
            if(SIMPLE_PAGE_PAGE_EVENT_CB_IS_EMPTY(temp_page, SIMPLE_PAGE_PAGE_EVENT_BACK)) {
                temp_link = simple_page_page_link_get_parent(page->current_page_link);
            } else {
                SIMPLE_PAGE_PAGE_DO_EVENT_CB(page, temp_page, SIMPLE_PAGE_PAGE_EVENT_BACK);
            }
        }
        break;
        case SIMPLE_PAGE_PAGE_EVENT_OK: {
            if(SIMPLE_PAGE_PAGE_EVENT_CB_IS_EMPTY(temp_page, SIMPLE_PAGE_PAGE_EVENT_OK)) {
                temp_link = simple_page_page_link_get_child(page->current_page_link);
            } else {
                SIMPLE_PAGE_PAGE_DO_EVENT_CB(page, temp_page, SIMPLE_PAGE_PAGE_EVENT_OK);
            }
        }
        break;
        case SIMPLE_PAGE_PAGE_EVENT_DRAW: {
            temp_link = page->current_page_link;
        }
        break;
        default: {
            simple_page_process_page_event(page, (simple_page_event_t)(evt - _SIMPLE_PAGE_PAGE_EVENT_END));
//            rt_kprintf("unknown evet:%d\r\n", evt);
        }
        break;
    }
    if(RT_NULL != temp_link) {
        if(evt == SIMPLE_PAGE_PAGE_EVENT_DRAW) {
            SIMPLE_PAGE_PAGE_DO_EVENT_CB(page, temp_page, SIMPLE_PAGE_PAGE_EVENT_DRAW);
        } else {
            SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_OUT);
            page->current_page_link = temp_link;
            SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_IN);
        }
    }
}

simple_page_t simple_page_create(simple_page_event_wait_t event_wait)
{
    simple_page_t page;
    page = rt_malloc(SIMPLE_PAGE_SIZE);
    if(page == RT_NULL) {
        return RT_NULL;
    }
    rt_memset(page, 0, SIMPLE_PAGE_SIZE);
    page->event_wait = event_wait;
    return page;
}

void simple_page_add_event(simple_page_t page, simple_page_event_t evt, simple_page_event_callback_t cb, void *param)
{
    if(page == RT_NULL) {
        return ;
    }
    if(IS_SIMPLE_PAGE_EVENT(evt)) {
        if(evt == SIMPLE_PAGE_EVENT_ALL) {
            for(int i=0; i<SIMPLE_PAGE_EVENT_NUM; i++) {
                page->fn_evt_cb_tab[i] = cb;
                page->fn_evt_param_tab[i] = param;
            }
        } else {
            page->fn_evt_cb_tab[evt] = cb;
            page->fn_evt_param_tab[evt] = param;
        }
    }
}
void simple_page_remove_event(simple_page_t page, simple_page_event_t evt)
{
    simple_page_add_event(page, evt, RT_NULL, RT_NULL);
}

void simple_page_page_destroy(simple_page_t page, simple_page_page_t page_page)
{
    for(simple_page_page_link_t node = page_page->link.next, temp = node->next; node != &page_page->link; node = temp, temp = temp->next) {
        simple_page_page_t temp_page_page = simple_page_page_get_from_link(node);
        if(node->child) {
            simple_page_page_destroy(page, simple_page_page_get_from_link(node->child));
        }
        SIMPLE_PAGE_PAGE_DO_EVENT_CB(page, temp_page_page, SIMPLE_PAGE_PAGE_EVENT_DESTROY);
        rt_free(temp_page_page);
    }
    rt_free(page_page);
}

void simple_page_destroy(simple_page_t page)
{
    simple_page_page_t page_page = simple_page_page_get_from_link(page->home_link);;
    simple_page_page_destroy(page, page_page);
    SIMPLE_PAGE_DO_EVENT_CB(page, SIMPLE_PAGE_EVENT_DESTROY);
    rt_free(page);
}

void simple_page_add_event_wait(simple_page_t page, simple_page_event_wait_t event_wait)
{
    page->event_wait = event_wait;
}

void simple_page_set_home_page_without_event(simple_page_t page, simple_page_page_t page_page)
{
    if(page == RT_NULL) {
        return ;
    }
    page->home_link = simple_page_page_get_to_link(page_page);
}

void simple_page_set_home_page(simple_page_t page, simple_page_page_t page_page)
{
    if(page == RT_NULL) {
        return ;
    }
    SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_OUT);
    page->home_link = simple_page_page_get_to_link(page_page);
    SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_IN);
}

simple_page_page_t simple_page_get_home_page(simple_page_t page)
{
    if(page == RT_NULL) {
        return RT_NULL;
    }
    return simple_page_page_get_from_link(page->home_link);
}

void simple_page_set_current_page_without_event(simple_page_t page, simple_page_page_t current_page)
{
    if(page == RT_NULL) {
        return ;
    }
    page->current_page_link = simple_page_page_get_to_link(current_page);
}

void simple_page_set_current_page(simple_page_t page, simple_page_page_t current_page)
{
    if(page == RT_NULL) {
        return ;
    }
    SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_OUT);
    page->current_page_link = simple_page_page_get_to_link(current_page);
    SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_IN);
}

simple_page_page_t simple_page_get_current_page(simple_page_t page)
{
    return simple_page_page_get_from_link(page->current_page_link);
}

void simple_page_go_next_page(simple_page_t page)
{
    if(page == RT_NULL) {
        return ;
    }
    if(page->current_page_link == RT_NULL) {
        page->current_page_link = page->home_link;
        SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_IN);
    } else {
        SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_OUT);
        page->current_page_link = simple_page_page_link_get_next(page->current_page_link);
        SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_IN);
    }
}

void simple_page_go_prev_page(simple_page_t page)
{
    if(page == RT_NULL) {
        return ;
    }
    if(page->current_page_link == RT_NULL) {
        page->current_page_link = page->home_link;
        SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_IN);
    } else {
        SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_OUT);
        page->current_page_link = simple_page_page_link_get_prev(page->current_page_link);
        SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_IN);
    }
}

void simple_page_go_child_page(simple_page_t page)
{
    if(page == RT_NULL) {
        return ;
    }
    if(page->current_page_link == RT_NULL) {
        page->current_page_link = page->home_link;
        SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_IN);
    } else {
        SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_OUT);
        page->current_page_link = simple_page_page_link_get_child(page->current_page_link);
        SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_IN);
    }
}

void simple_page_go_parent_page(simple_page_t page)
{
    if(page == RT_NULL) {
        return ;
    }
    if(page->current_page_link == RT_NULL) {
        page->current_page_link = page->home_link;
        SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_IN);
    } else {
        SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_OUT);
        page->current_page_link = simple_page_page_link_get_parent(page->current_page_link);
        SIMPLE_PAGE_PAGE_DO_EVENT_CB_FROM_LINK(page, page->current_page_link, SIMPLE_PAGE_PAGE_EVENT_IN);
    }
}


